package cn.zwx.activiti.example.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.zwx.activiti.example.common.enums.ErrorEnum;
import cn.zwx.activiti.example.common.exception.BizException;
import cn.zwx.activiti.example.mapper.LeaveMapper;
import cn.zwx.activiti.example.mapper.UserMapper;
import cn.zwx.activiti.example.model.po.Leave;
import cn.zwx.activiti.example.model.po.UserPO;
import cn.zwx.activiti.example.model.req.*;
import cn.zwx.activiti.example.model.resp.QueryInstancesListResp;
import cn.zwx.activiti.example.model.resp.QueryVariablesResp;
import cn.zwx.activiti.example.model.resp.StartProcessResp;
import cn.zwx.activiti.example.service.ProcessInstanceService;
import cn.zwx.activiti.example.service.UserService;
import com.google.common.collect.Sets;
import org.activiti.api.model.shared.model.VariableInstance;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.model.payloads.GetProcessDefinitionsPayload;
import org.activiti.api.process.model.payloads.GetProcessInstancesPayload;
import org.activiti.api.process.model.payloads.ResumeProcessPayload;
import org.activiti.api.process.model.payloads.SuspendProcessPayload;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.runtime.api.query.impl.PageImpl;
import org.apache.regexp.RE;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.math.BigDecimal;
import java.util.*;


/**
 * @description:
 * @projectName:activiti-example
 * @see:cn.zwx.activiti.example.service.impl
 * @author:zhangwenxue
 * @createTime:2021/5/30 0:03
 * @version:1.0
 */
@Service
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    private Logger logger = LoggerFactory.getLogger(ProcessInstanceServiceImpl.class);
    @Autowired
    private ProcessRuntime processRuntime;
    @Autowired
    private RepositoryService repositoryService;

    /**
     * @description 启动流程实例
     * TODO 真是场景 业务和审核流是分离的
     * 业务数据 比如 请假申请 请假人 天数 请假类型
     * 应该是提交审核流就会提交过来  这里应该是启动流程+填写请假单一起做作为一个接口
     * @return cn.zwx.activiti.example.model.resp.StartProcessResp
     * @author zhangwenxue
     * @createTime 2021/6/1 22:05
     **/
    @Override
    public StartProcessResp startProcess(StartProcessReq req) {
        // 获取参数
        Map<String, Object> instanceVariable = req.getInstanceVariable();
        // 2.部署工作流
        ProcessInstance processInstance = null;
        try{
            processInstance = processRuntime.start(ProcessPayloadBuilder
                    .start()
                    .withProcessDefinitionKey(req.getProcessDefinitionKey())
                    .withName(req.getInstanceName())
                    .withBusinessKey(req.getBusinessKey())
                    .withVariables(instanceVariable)
                    .build());
        }catch (Exception e){
            logger.error("启动流程实例 失敗.",e);
            throw new BizException(ErrorEnum.START_INSTANCE_FAILED.getErrorCode(),ErrorEnum.START_INSTANCE_FAILED.getErrorMsg());
        }

        // 3.返回参数
        StartProcessResp resp = new StartProcessResp();
        resp.setProcessInstanceId(processInstance.getId());
        resp.setProcessInstanceName(processInstance.getName());
        return resp;
    }

    /**
     * @description 获取流程实例列表
     * @return org.activiti.api.runtime.shared.query.Page<cn.zwx.activiti.example.model.resp.QueryInstancesListResp>
     * @author zhangwenxue
     * @createTime 2021/6/1 23:25
     **/
    @Override
    public Page<QueryInstancesListResp> getInstancesList(QueryInstancesListReq req) {
        GetProcessInstancesPayload payload = null;
        if (StrUtil.isNotBlank(req.getProcessDefinitionKey())){
            payload = new GetProcessInstancesPayload();
            payload.setProcessDefinitionKeys(Sets.newHashSet(req.getProcessDefinitionKey()));
        }
        Page<ProcessInstance> processDefinitionPage = Objects.nonNull(payload) ? processRuntime.processInstances(Pageable.of(req.getStartIndex(), req.getEndIndex()),
                payload)
                : processRuntime.processInstances(Pageable.of(req.getStartIndex(), req.getEndIndex()));
        List<ProcessInstance> list = processDefinitionPage.getContent();
        if (CollectionUtil.isEmpty(list)){
            return new PageImpl<>(new ArrayList<>(), BigDecimal.ZERO.intValue());
        }
        list.sort((y,x)->x.getStartDate().toString().compareTo(y.getStartDate().toString()));

        List<QueryInstancesListResp> returnList = CollectionUtil.newArrayList();
        for(ProcessInstance pi:list){
            QueryInstancesListResp resp = new QueryInstancesListResp();
            resp.setId(pi.getId());
            resp.setName(pi.getName());
            resp.setProcessDefinitionId(pi.getProcessDefinitionId());
            resp.setProcessDefinitionKey(pi.getProcessDefinitionKey());
            resp.setStartDate(DateUtil.format(pi.getStartDate(), DatePattern.NORM_DATETIME_PATTERN));
            resp.setStatus(pi.getStatus());
            //因为processRuntime.processDefinition("流程部署ID")查询的结果没有部署流程与部署ID，所以用repositoryService查询
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(pi.getProcessDefinitionId())
                    .singleResult();
            if (Objects.nonNull(pd)){
                resp.setDeploymentId(pd.getDeploymentId());
                resp.setResourceName(pd.getResourceName());
            }
            returnList.add(resp);
        }
        return new PageImpl<>(returnList,returnList.size());
    }

    /**
     * @description 流程挂起
     * @return void
     * @author zhangwenxue
     * @createTime 2021/6/2 20:33
     **/
    @Override
    public void suspendInstance(SuspendInstanceReq req) {
        // 构建请求参数
        SuspendProcessPayload build = ProcessPayloadBuilder
                .suspend()
                .withProcessInstanceId(req.getProcessId())
                .build();
        try {
            ProcessInstance resume = processRuntime.suspend(build);
            logger.info("挂起流程实例成功!,实例名称:{},实例id:{}",resume.getId(),resume.getName());
        }catch (Exception e){
            logger.error("挂起流程实例失败,实例id:{}",req.getProcessId(),e);
            throw new BizException(ErrorEnum.SUSPEND_INSTANCE_FAILED.getErrorCode(),ErrorEnum.SUSPEND_INSTANCE_FAILED.getErrorMsg());
        }
    }

    /**
     * @description 流程激活
     * @return void
     * @author zhangwenxue
     * @createTime 2021/6/2 20:40
     **/
    @Override
    public void resumeInstance(ResumeInstanceReq req) {
        // 组装参数
        ResumeProcessPayload build = ProcessPayloadBuilder.resume().withProcessInstanceId(req.getProcessId()).build();
        try {
            ProcessInstance resume = processRuntime.resume(build);
            logger.info("流程激活实例成功!,实例名称:{},实例id:{}",resume.getId(),resume.getName());
        }catch (Exception e){
            logger.error("流程激活实例失败,实例id:{}",req.getProcessId(),e);
            throw new BizException(ErrorEnum.RESUME_INSTANCE_FAILED.getErrorCode(),ErrorEnum.RESUME_INSTANCE_FAILED.getErrorMsg());
        }
    }


    /**
     * @description 获取流程实例参数
     * @return java.util.List<cn.zwx.activiti.example.model.resp.QueryVariablesResp>
     * @author zhangwenxue
     * @createTime 2021/6/2 20:49
     **/
    @Override
    public List<QueryVariablesResp> variables(QueryVariablesReq req) {
        List<VariableInstance> variableInstance = CollectionUtil.newArrayList();
        // 请求接口
        try {
             variableInstance = processRuntime.variables(ProcessPayloadBuilder
                            .variables()
                            .withProcessInstanceId(req.getProcessId())
                            .build());
        }catch (Exception e){
            logger.error("获取流程实例参数失败,实例id:{}",req.getProcessId(),e);
            throw new BizException(ErrorEnum.QUERY_VARIABLES_INSTANCE_FAILED.getErrorCode(),ErrorEnum.QUERY_VARIABLES_INSTANCE_FAILED.getErrorMsg());
        }

        List<QueryVariablesResp> returnList = CollectionUtil.newArrayList();
        if (CollectionUtil.isEmpty(variableInstance)){
            return returnList;
        }
        //参数转换
        for (VariableInstance queryVariablesResp : variableInstance) {
            QueryVariablesResp variable = new QueryVariablesResp();
            variable.setName(queryVariablesResp.getName());
            variable.setProcessInstanceId(queryVariablesResp.getProcessInstanceId());
            variable.setTaskId(queryVariablesResp.getTaskId());
            variable.setType(queryVariablesResp.getType());
            variable.setValue(queryVariablesResp.getValue());
            returnList.add(variable);
        }
        return returnList;
    }

    /**
     * @description 删除流程实例
     * @return void
     * @author zhangwenxue
     * @createTime 2021/6/2 21:02
     **/
    @Override
    public void deleteInstance(DeleteInstanceReq req) {
        // 请求接口
        try {
            ProcessInstance processInstance = processRuntime.delete(ProcessPayloadBuilder
                    .delete()
                    .withProcessInstanceId(req.getProcessId())
                    .withReason(req.getReason())
                    .build()
            );
            logger.info("删除流程实例成功!,实例名称:{},实例id:{}",processInstance.getName(),processInstance.getId());
        }catch (Exception e){
            logger.error("删除流程实例失败,实例id:{}",req.getProcessId(),e);
            throw new BizException(ErrorEnum.DELETE_INSTANCE_FAILED.getErrorCode(),ErrorEnum.DELETE_INSTANCE_FAILED.getErrorMsg());
        }

    }
}
